package com.nervenets.general.component;

import com.fasterxml.classmate.ResolvedType;
import com.google.common.base.Optional;
import com.nervenets.general.enumeration.Enums;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.Annotations;
import springfox.documentation.service.AllowableListValues;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
import springfox.documentation.swagger.schema.ApiModelProperties;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static com.nervenets.general.i18n.I18nTranslator.braceTranslate;

@Profile({"swagger"})
@Slf4j
@Component
@Primary
public class SwaggerEnumPropertyPlugin implements ModelPropertyBuilderPlugin {
    @Override
    public void apply(ModelPropertyContext context) {
        Optional<ApiModelProperty> annotation = Optional.absent();
        if (context.getAnnotatedElement().isPresent()) {
            annotation = annotation.or(ApiModelProperties.findApiModePropertyAnnotation(context.getAnnotatedElement().get()));
        }

        if (context.getBeanPropertyDefinition().isPresent()) {
            annotation = annotation.or(Annotations.findPropertyAnnotation(context.getBeanPropertyDefinition().get(), ApiModelProperty.class));
        }

        final Class<?> rawPrimaryType = context.getBeanPropertyDefinition().get().getRawPrimaryType();
        if (annotation.isPresent()) {
            if (Enums.class.isAssignableFrom(rawPrimaryType)) {
                final Enums[] values = (Enums[]) rawPrimaryType.getEnumConstants();
                final List<String> displayValues = Arrays.stream(values).map(e -> e.getValue() + ":" + e.getText()).collect(Collectors.toList());
                final AllowableListValues allowableListValues = new AllowableListValues(Arrays.stream(values).map(Enums::getValue).collect(Collectors.toList()), rawPrimaryType.getTypeName());
                final ResolvedType resolvedType = context.getResolver().resolve(String.class);
                context.getBuilder()
                        .description(annotation.get().value() + "\n\nEnums:" + rawPrimaryType.getSimpleName() + "\n" + String.join("，", displayValues))
                        .allowableValues(allowableListValues)
                        .type(resolvedType);
            } else {
                context.getBuilder()
                        .description(braceTranslate(annotation.get().value()));
            }
        }
    }

    @Override
    public boolean supports(DocumentationType documentationType) {
        return true;
    }
}
